﻿#include "stdafx.h"
#include "qg/qgenv.h"
#include "qg/qgasv.h"
#include "qg/qgimage.h"
#include "qg_subtex.h"

//////////////////////////////////////////////////////////////////////////
// 텍스쳐
KIMPL_SUBCLASS_ABSTRACT(qgTexture, "AbstractTexture");

// 해시용 클래스
class FakeHashedTexture : public qgTexture
{
	KCH_BASE(FakeHashedTexture, qgTexture);

public:
	static kconstpointer BaseKey(kNodeBase* ptr)
	{
		FakeHashedTexture* self = (FakeHashedTexture*)ptr;
		return &self->_key;
	}

	static bool BaseEq(kNodeBase* ptr, const KeyType& k2)
	{
		FakeHashedTexture* self = (FakeHashedTexture*)ptr;
		const KeyType& k1 = self->_key;

		return k_stricmp(k1, k2) == 0;
	}

	static ksize_t KeyHash(const KeyType& key)
	{
		return k_strihash(key);
	}
};

//
void qgTexture::TypeInitialize(kType type) throw()
{
	kTsb t(type);
	t.SetFuncKey(&FakeHashedTexture::BaseKey);
	t.SetFuncEq((khEqFunc)&FakeHashedTexture::BaseEq);
	t.SetFuncHash((khHashFunc)&FakeHashedTexture::KeyHash);
}

//
qgTexture::qgTexture()
: _lock_mip(-1), _lock_face(QGTXFX_NONE)
{
}

//
qgTexture::~qgTexture()
{
	k_delete(_key);
}


//////////////////////////////////////////////////////////////////////////
// 텍스쳐 모션

//
void qgSubTextureMotion::CleanStreams()
{
	// 다 필요엄쓰니 지우자
	k_delete_reset(&_mt_sizes);
	k_delete_reset(&_mt_locations);
	k_delete_reset(&_mt_readdata);
	kobj_reset(&_mt_ucmimage);
	kobj_reset(&_mt_stm);

	// 토탈도 지우자
	_mt_totallen = 0;
}

//
bool qgSubTextureMotion::ParseMotion(kStream* stm, bool preload)
{
	_mt_stm = stm;

	// 나중에 통합으로 바꿔야 함
	qgFwHsItem him;
	if (stm->Read(&him, 0, sizeof(qgFwHsHead)) != sizeof(qgFwHsHead) ||
		him.hdr != K_MAKEFOURCC('H', 'X', 'P', ' ') ||
		him.type != QGHI_HXP ||
		him.version != 1)
		return false;

	//
	qgFwHxp hxp;
	if (stm->Read(&hxp, 0, sizeof(qgFwHxp)) != sizeof(qgFwHxp) ||
		hxp.count == 0 ||
		hxp.framepersecond == 0 ||
		hxp.width == 0 ||
		hxp.height == 0 ||
		hxp.bpp != 4)
		return false;

	// 개별 길이 저장
	_mt_sizes = k_new(hxp.count, kint);
	if (stm->Read(_mt_sizes, 0, sizeof(kint)* hxp.count) != (kint)(sizeof(kint)* hxp.count))
		return false;

	// 누적 seek 위치 저장
	_mt_locations = k_new(hxp.count, kint);
	_mt_locations[0] = (kint)stm->Tell();
	kint readsize = _mt_sizes[0];

	for (kuint i = 1; i < hxp.count; i++)
	{
		_mt_locations[i] = _mt_locations[i - 1] + _mt_sizes[i - 1];

		if (readsize < _mt_sizes[i])
			readsize = _mt_sizes[i];
	}

	// 기본 프로퍼티
	_mt_totallen = him.length;

	_mt_count = hxp.count;
	_mt_fps = hxp.framepersecond;
	_mt_bpp = hxp.bpp;

	_mt_fvs = (float)_mt_fps;
	_mt_len = (float)(1.0f / _mt_fvs * _mt_count);
	_mt_adv = 0.0f;
	_mt_index = K_MAX_UINT;
	_mt_next = K_MAX_UINT;

	_mt_loop = false;
	_mt_pause = false;
	_mt_range.umin = 0;
	_mt_range.umax = _mt_count - 1;

	_mt_readdata = k_new(readsize, kbyte);

	_mt_ucmimage = kobj_new(qgImage, hxp.width, hxp.height, QGCF32_RGBA);

	// 너무 크면 좀 그렇슴
	if (preload)
	{
		klong size = stm->GetSize();
		if (size < 134217728)	// 2048*2048*32 = 128메가
			_mt_preimgs.Resize(_mt_count);
	}

	return true;
}

//
bool qgSubTextureMotion::UpdateStreamImage(kuint image)
{
	if (!_mt_ucmimage)
		return false;

	if (!_mt_stm->Seek(_mt_locations[image], KSEEK_BEGIN))
		return false;

	kint size = _mt_sizes[image];
	if (_mt_stm->Read(_mt_readdata, 0, size) != size)
		return false;

	if (!k_zucmprsb(_mt_ucmimage->GetData(), _mt_ucmimage->GetSize(), _mt_readdata, size))
		return false;

	return true;
}

//
bool qgSubTextureMotion::UpdateImage(kuint image)
{
	if (image < _mt_range.umin)
		image = _mt_range.umin;
	else if (image > _mt_range.umax)
		image = _mt_range.umax;

	if (image == _mt_index)
		return false;

	_mt_index = image;

	return UpdateStreamImage(image);
}

//
bool qgSubTextureMotion::UpdateImage()
{
	return UpdateImage(_mt_next);
}

//
bool qgSubTextureMotion::UpdateAdvance(float f)
{
	_mt_adv += f;

	_mt_next = _mt_range.umin + (kuint)(_mt_adv * _mt_fvs);

	return _mt_adv < _mt_len;
}

//
void qgSubTextureMotion::ResetLoop()
{
	float f = _mt_adv;

	for (;;)
	{
		f -= _mt_len;
		if (f < _mt_len)
			break;
	}

	_mt_adv = f;
	_mt_next = _mt_range.umin + (kuint)(_mt_adv * _mt_fvs);
}

//
void qgSubTextureMotion::Reset()
{
	_mt_adv = 0.0f;
}

//
void qgSubTextureMotion::SetFixedLength(float f)
{
	_mt_len = f;
	_mt_fixlen = f;
}

//
void qgSubTextureMotion::SetFps(kint v)
{
	k_return_if_fail(v >= 1);

	_mt_fps = v;
	_mt_fvs = (float)v;

	if (_mt_fixlen > 0.0f)
		_mt_len = _mt_fixlen;
	else
	{
		kuint tmp = _mt_range.umax - _mt_range.umin + 1;
		_mt_len = 1.0f / _mt_fvs * (float)tmp;
	}
}

//
void qgSubTextureMotion::SetFrameRange(kint smin, kint smax)
{
	_mt_range.umin = K_CLAMP((kuint)smin, 0, _mt_count - 1);
	_mt_range.umax = K_CLAMP((kuint)smax, 0, _mt_count - 1);

	if (_mt_range.umax == 0)
		_mt_range.umax = _mt_count - 1;

	if (_mt_fixlen > 0.0f)
		_mt_len = _mt_fixlen;
	else
	{
		kuint tmp = _mt_range.umax - _mt_range.umin + 1;
		_mt_len = 1.0f / _mt_fvs * (float)tmp;
	}
}

//
void qgSubTextureMotion::SetPosition(float pos)
{
	if (pos < 0.0f || pos > _mt_len)
		return;

	_mt_adv = pos;
}

//
void qgSubTextureMotion::SetPlayMode(bool isplay)
{
	_mt_pause = !isplay;
}

//
void qgSubTextureMotion::TurnLoop(bool isloop)
{
	_mt_loop = isloop;
}

//
bool qgSubTextureMotion::ManualUpdate(float adv, bool& res, kuintptr& ptr)
{
	// res = 리소스를 업데이트하나... 스트리밍일 경우 true는 텍스쳐 만들기
	// ptr = 리소스 데이터... 리스트 캐시일 경우 텍스쳐 리소스 포인터

	if (!UpdateAdvance(adv))
	{
		if (!_mt_loop)
		{
			ptr = 0;
			res = false;
			return false;
		}

		ResetLoop();
	}

	if (_mt_preimgs.IsEmpty())
	{
		// 스트리밍
		ptr = 0;

		if (_mt_next == _mt_index ||
			_mt_next > _mt_range.umax)
		{
			res = false;
			return true;
		}

		_mt_index = _mt_next;

		if (!UpdateStreamImage(_mt_next))
		{
			res = false;
			return false;
		}
		else
		{
			res = true;
			return true;
		}
	}
	else
	{
		if (_mt_next > _mt_range.umax)
			_mt_index = _mt_range.umax;
		else
			_mt_index = _mt_next;

		ptr = _mt_preimgs[_mt_index];
		res = false;

		return true;
	}
}

//
qgImage* qgSubTextureMotion::MotionImage(kint index)
{
	if (!_mt_stm)
		return NULL;

	if (index < 0)
		index = _mt_index;

	if (index < 0 || (kuint)index >= _mt_count)
		return NULL;

	//
	if (!_mt_stm->Seek(_mt_locations[index], KSEEK_BEGIN))
		return NULL;

	kint size = _mt_sizes[index];
	if (_mt_stm->Read(_mt_readdata, 0, size) != size)
		return NULL;

	//
	qgImage* img = kobj_new(qgImage, _mt_ucmimage->GetWidth(), _mt_ucmimage->GetHeight(), QGCF32_RGBA);

	if (!k_zucmprsb(img->GetData(), img->GetSize(), _mt_readdata, size))
	{
		img->Unload();
		return NULL;
	}

	return img;
}


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼
KIMPL_SUBCLASS_ABSTRACT(qgDepthBuffer, "AbstractDepthBuffer");

